package com.jaiku.api
{
	import com.adobe.net.DynamicURLLoader;
	import com.adobe.serialization.json.JSON;
	import com.adobe.serialization.json.JSONParseError;
	import com.adobe.webapis.URLLoaderBase;
	import com.jaiku.api.events.JaikuResultEvent;
	import com.jaiku.api.results.JaikuNotesResult;
	import com.jaiku.api.results.JaikuUsersResult;
	
	import flash.events.Event;
	import flash.net.URLRequest;
	import flash.net.URLRequestMethod;
	import flash.net.URLVariables;
		
	/**
	 * The Class is an ActionScript 3 Library for use against the Jaiku API 
	 *
	 * @langversion ActionScript 3.0
	 * @playerversion Flash 9
	 * 
	 * @author Jarek Szczepanski
	 * 
	 * @see http://code.google.com/p/as3jaikulib/
	 * 
	 */
	public class JaikuService extends URLLoaderBase
	{
		private const END_POINT_START:String = "http://";
		private const END_POINT_MIDDLE:String = "jaiku.com/";
		private const END_POINT_FINISH:String = "feed/json/";
	
		private var _jaikuResultEvent:JaikuResultEvent;
		
		private var _appName:String;
		private var _username:String;
		private var _personal_key:String;
		private var _comments:Boolean = true;
			
		/**
		 * Constructor
		 *  
		 * @param username Jaiku username
		 * @param personal_key Personal API key
		 * 
		 * @see http://api.jaiku.com/
		 * 
		 */
		public function JaikuService(username:String = "", personal_key:String = ""):void
		{
			_username = username;
			_personal_key = personal_key;
		} 	
		public function verifyAuth():void
		{			
			var urlRequest:URLRequest;
			var urlLoader:DynamicURLLoader = getURLLoader();
			
			urlRequest = getURLRequest(END_POINT_START + _username + "." + END_POINT_MIDDLE + "contacts/" + END_POINT_FINISH, URLRequestMethod.GET);
			
			urlLoader.addEventListener(Event.COMPLETE, onVerifyAuth);
			urlLoader.load(urlRequest);				
		}
		private function onVerifyAuth(evt:Event):void
		{
			try 
			{
				var rawData:Object = JSON.decode(String(evt.currentTarget.data));

				_jaikuResultEvent = new JaikuResultEvent(JaikuResultEvent.ON_VERIFY_AUTH);
				_jaikuResultEvent.data = "OK";
			}
			catch (err:JSONParseError)
			{
				_jaikuResultEvent = new JaikuResultEvent(JaikuResultEvent.ON_VERIFY_AUTH);
				_jaikuResultEvent.data = "Failed";
			}	
			dispatchEvent(_jaikuResultEvent);
		}			
		/**
		 * Get a list of all recent public notes.  
		 * 
		 */
		public function getPublicStream():void
		{
			var urlRequest:URLRequest;
			var urlLoader:DynamicURLLoader = getURLLoader();

			urlRequest = getURLRequest(END_POINT_START + END_POINT_MIDDLE + END_POINT_FINISH, URLRequestMethod.GET);
			
			urlLoader.addEventListener(Event.COMPLETE, onGetPublicStream);
			urlLoader.load(urlRequest);
		}
		private function onGetPublicStream(evt:Event):void
		{
			var jaikuResult:JaikuNotesResult = JaikuResultParser.parseNotes(String(evt.currentTarget.data), _comments);
			
			_jaikuResultEvent = new JaikuResultEvent(JaikuResultEvent.ON_GET_PUBLIC_STREAM);
			_jaikuResultEvent.data = jaikuResult;
			
			dispatchEvent(_jaikuResultEvent);
		}		
		/**
		 * Get a list of notes for a user contacts.
		 * 
		 * @param getComments Filter comments
		 * 
		 */
		public function getContactsStream(getComments:Boolean):void
		{
			_comments = getComments;
			var urlRequest:URLRequest;
			var urlLoader:DynamicURLLoader = getURLLoader();

			urlRequest = getURLRequest(END_POINT_START + _username + "." + END_POINT_MIDDLE + "contacts/" + END_POINT_FINISH, URLRequestMethod.GET);
			
			urlLoader.addEventListener(Event.COMPLETE, onGetContactsStream);
			urlLoader.load(urlRequest);
		}
		private function onGetContactsStream(evt:Event):void
		{
			try 
			{
				var rawData:Object = JSON.decode(String(evt.currentTarget.data));
				var jaikuResult:JaikuNotesResult = JaikuResultParser.parseNotes(rawData, _comments);
		
				_jaikuResultEvent = new JaikuResultEvent(JaikuResultEvent.ON_GET_CONTACTS_STREAM);
				_jaikuResultEvent.data = jaikuResult;
			}
			catch (err:JSONParseError)
			{
				_jaikuResultEvent = new JaikuResultEvent(JaikuResultEvent.ERROR);
				_jaikuResultEvent.data = "Authentication failed!";
			}			
			
			dispatchEvent(_jaikuResultEvent);
		}
		/**
		 * Get a list of notes for a user.
		 *  
		 * @param username Username of a Jaiku user. 
		 * 
 		 */
		public function getUserStream(username:String):void
		{
			var urlRequest:URLRequest;
			var urlLoader:DynamicURLLoader = getURLLoader();

			urlRequest = getURLRequest(END_POINT_START + username + "." + END_POINT_MIDDLE + END_POINT_FINISH, URLRequestMethod.GET);
			
			urlLoader.addEventListener(Event.COMPLETE, onGetUserStream);
			urlLoader.load(urlRequest);
		}
		private function onGetUserStream(evt:Event):void
		{
			try 
			{
				var rawData:Object = JSON.decode(String(evt.currentTarget.data));
				var jaikuResult:JaikuNotesResult = JaikuResultParser.parseNotes(rawData, false);
		
				_jaikuResultEvent = new JaikuResultEvent(JaikuResultEvent.ON_GET_USER_STREAM);
				_jaikuResultEvent.data = jaikuResult;
			}
			catch (err:JSONParseError)
			{
				_jaikuResultEvent = new JaikuResultEvent(JaikuResultEvent.ERROR);
				_jaikuResultEvent.data = "Wrong username!";
			}			

			dispatchEvent(_jaikuResultEvent);
		}
		/**
		 * Get an specified presence from user.
		 *  
		 * @param username Username of a Jaiku user or channel. 
		 * @param presenceId Id of specified presence.
		 * @param channel  
		 * 
		 */
		public function getPresence(name:String, presenceId:int, channel:Boolean):void
		{
			var urlRequest:URLRequest;
			var urlLoader:DynamicURLLoader = getURLLoader();
			
			if (!channel)
				urlRequest = getURLRequest(END_POINT_START + name + "." + END_POINT_MIDDLE + "presence/" + presenceId + "/json", URLRequestMethod.GET);
			else
				urlRequest = getURLRequest(END_POINT_START + END_POINT_MIDDLE + "channel/" + name + "/presence/" + presenceId + "/json", URLRequestMethod.GET);

			urlLoader.addEventListener(Event.COMPLETE, onGetPresence);
			urlLoader.load(urlRequest);		
		}
		private function onGetPresence(evt:Event):void
		{
			try 
			{
				var rawData:Object = JSON.decode(String(evt.currentTarget.data));
				var jaikuResult:JaikuNotesResult = JaikuResultParser.parseNote(rawData);
		
				_jaikuResultEvent = new JaikuResultEvent(JaikuResultEvent.ON_GET_PRESENCE);
				_jaikuResultEvent.data = jaikuResult;
			}
			catch (err:JSONParseError)
			{
				_jaikuResultEvent = new JaikuResultEvent(JaikuResultEvent.ERROR);
				_jaikuResultEvent.data = "Wrong username or presence ID!";
			}			
			
			dispatchEvent(_jaikuResultEvent);		
		}
		/**
		 * Get an individual user's profile information.
		 *  
		 * @param username Username of a Jaiku user. 
		 * 
		 */
		public function getUserProfile(username:String):void
		{
			var urlRequest:URLRequest;
			var urlLoader:DynamicURLLoader = getURLLoader();

			urlRequest = getURLRequest(END_POINT_START + username + "." + END_POINT_MIDDLE + "json/", URLRequestMethod.GET);
			
			urlLoader.addEventListener(Event.COMPLETE, onGetUserProfile);
			urlLoader.load(urlRequest);	
		}
		private function onGetUserProfile(evt:Event):void
		{
			try 
			{
				var rawData:Object = JSON.decode(String(evt.currentTarget.data));
				var jaikuResult:JaikuUsersResult = JaikuResultParser.parseProfile(rawData);
				
				_jaikuResultEvent = new JaikuResultEvent(JaikuResultEvent.ON_GET_USER_PROFILE);
				_jaikuResultEvent.data = jaikuResult;
			}
			catch (err:JSONParseError)
			{
				_jaikuResultEvent = new JaikuResultEvent(JaikuResultEvent.ERROR);
				_jaikuResultEvent.data = "Wrong username!";
			}			

			dispatchEvent(_jaikuResultEvent);
		}
		/**
		 * Get a list of notes from specified channel.
		 * 
		 * @param channel Channel's name
		 * 
		 */
		public function getChannelStream(channel:String):void
		{
			var urlRequest:URLRequest;
			var urlLoader:DynamicURLLoader = getURLLoader();

			urlRequest = getURLRequest(END_POINT_START + END_POINT_MIDDLE + "channel/" + channel + "/" + END_POINT_FINISH, URLRequestMethod.GET);

			urlLoader.addEventListener(Event.COMPLETE, onGetChannelStream);
			urlLoader.load(urlRequest);			
		}
		private function onGetChannelStream(evt:Event):void
		{
			try 
			{
				var rawData:Object = JSON.decode(String(evt.currentTarget.data));
				var jaikuResult:JaikuNotesResult = JaikuResultParser.parseNotes(rawData, false);
				
				_jaikuResultEvent = new JaikuResultEvent(JaikuResultEvent.ON_GET_CHANNEL_STREAM);
				_jaikuResultEvent.data = jaikuResult;
			}
			catch (err:JSONParseError)
			{
				_jaikuResultEvent = new JaikuResultEvent(JaikuResultEvent.ERROR);
				_jaikuResultEvent.data = "Wrong channel's name!";
			}			

			dispatchEvent(_jaikuResultEvent);
		}
		/**
		 * Post a presence.
		 *  
		 * @param message Message text. 
		 * 
		 */
		public function postPresence(message:String, icon:int=300):void
		{
			var urlRequest:URLRequest;
			var urlLoader:DynamicURLLoader = getURLLoader();
			var urlVariables:URLVariables = new URLVariables();
			
			urlVariables.message = message;
			urlVariables.icon = icon; 

			urlRequest = getURLRequest("http://api.jaiku.com/json", URLRequestMethod.POST, urlVariables);
			
			urlLoader.addEventListener(Event.COMPLETE, onPostPresence);
			urlLoader.load(urlRequest);			
		}
		private function onPostPresence(evt:Event):void
		{
			try 
			{
				var response:Object = JSON.decode(String(evt.currentTarget.data));
				
				if (response.status == "error")
				{
					_jaikuResultEvent = new JaikuResultEvent(JaikuResultEvent.ON_POST_MESSAGE);
					_jaikuResultEvent.data = response.message;
				}
				else
				{
					_jaikuResultEvent = new JaikuResultEvent(JaikuResultEvent.ON_POST_MESSAGE);
					_jaikuResultEvent.data = "Probably everything is ok. But nobody is certain...";
				}
			}
			catch (err:JSONParseError)
			{
				_jaikuResultEvent = new JaikuResultEvent(JaikuResultEvent.ERROR);
				_jaikuResultEvent.data = "Post failed!";
			}			
			
			dispatchEvent(_jaikuResultEvent);	
		}			
		/**
		 * Post a comment. Workaround to obtain <b>_nonce</b> token. 
		 *  
		 * @param message Comment text. 
		 * 
		 */
		public function postComment(name:String, message:String, presenceId:int, channel:Boolean):void
		{
			var urlRequest:URLRequest;
			var urlLoader:DynamicURLLoader = getURLLoader();
			
			if (!channel)
				urlRequest = getURLRequest(END_POINT_START + name + "." + END_POINT_MIDDLE + "presence/" + presenceId, URLRequestMethod.GET);
			else
				urlRequest = getURLRequest(END_POINT_START + END_POINT_MIDDLE + "channel/" + name + "/presence/" + presenceId, URLRequestMethod.GET);

			urlLoader.addEventListener(Event.COMPLETE, function (evt:Event) : void {
				onPreparePostComment(evt, name, message, presenceId, channel);
			});
			urlLoader.load(urlRequest);			
		}
		private function onPreparePostComment(evt:Event, name:String, message:String, presenceId:int, channel:Boolean):void
		{
			var output:String = evt.currentTarget.data; 
			var pattern:RegExp = new RegExp('<input type="hidden" name="_nonce" value="(.*?)" />', 'gi'); 
			
			var urlRequest:URLRequest;
			var urlLoader:DynamicURLLoader = getURLLoader();
			var urlVariables:URLVariables = new URLVariables();

			urlVariables.comment = message;
			urlVariables._nonce = pattern.exec(output)[1];
			
			if (!channel)
				urlRequest = getURLRequest(END_POINT_START + name + "." + END_POINT_MIDDLE + "presence/" + presenceId, "COMMENT", urlVariables);
			else
				urlRequest = getURLRequest(END_POINT_START + END_POINT_MIDDLE + "channel/" + name + "/presence/" + presenceId, "COMMENT", urlVariables);

			
			urlLoader.addEventListener(Event.COMPLETE, onPostComment);
			urlLoader.load(urlRequest);

		}	
		private function onPostComment(evt:Event):void
		{
			//trace(evt.currentTarget.data);
			_jaikuResultEvent = new JaikuResultEvent(JaikuResultEvent.ON_POST_COMMENT);
			_jaikuResultEvent.data = "Probably everything is ok. But nobody is certain...";		
			
			dispatchEvent(_jaikuResultEvent);				
		}
		/**
		 * Generate new URL Request
		 *  
		 * @param url Path to API url
		 * @param method 
		 * @return 
		 * 
		 */
		private function getURLRequest(url:String, method:String, urlVariables:URLVariables = null):URLRequest{
			var urlRequest:URLRequest = new URLRequest();
			urlRequest.url = url + "?user=" + _username + "&personal_key=" + _personal_key;
//			urlRequest.userAgent = this._appName;
//			urlRequest.cacheResponse = false;

			switch(method){
				case "GET":
					urlRequest.contentType = "application/json";
					urlRequest.method = URLRequestMethod.GET;
					break;
				case "POST":
					urlRequest.method = URLRequestMethod.POST;
					urlRequest.data = "method=presence.send&message=" + urlVariables.message;
					urlRequest.data += "&icon=" + urlVariables.icon;
					break;
				case "COMMENT":
					urlRequest.method = URLRequestMethod.POST;
					urlRequest.data = urlVariables;
					break;
			}
			return urlRequest;
		}
	}
}